/*
 * occupation_mgr.cpp
 *
 *  Created on: Nov 13, 2013
 *      Author: killerg
 */

#include "../server_deps.h"
#include "../server.h"
#include "../server_cfg.h"
#include "../client_session.h"
#include "../server_console.h"

#include "occupation_mgr.h"
#include "proto_mgr.h"
#include "player_mgr.h"
#include "mail_mgr.h"

#include "share/clock.h"
#include "share/util.h"
#include "share/msg.h"
#include "share/net_s.h"

bool occupation_mgr::init() {
	if (!g_console->_occupy_enabled) {
		UTIL_LOG(I, "occupy disabled");
		return true;
	}

    bool res = g_dbf->get(
            fmt_cstr("_occuption:%d", g_cfg->ServerId),
            [this, &res](db_error_t err, const Json::Value &value){
                switch (err) {
                case DBE_Success:
                    res = init(value);
                    break;
                case DBE_NoExist:
                    UTIL_LOG(I, "occupation data not exist");
                    break;
                default:
                    UTIL_LOG(E, "初始化occupation cb error %s", db_strerror(err));
                    break;
                }
            });
    if (!res) {
        UTIL_LOG(E, "occupation_mgr load from db error");
        return false;
    }

    g_dbf->wait();
    _inited = res;
    return res;
}

bool occupation_mgr::init(const Json::Value &json) {
    const Json::Value &json_arr = json["regions"];

    for (auto itr = json_arr.begin(); itr != json_arr.end(); ++itr) {
        const Json::Value& region_json = *itr;

        int32_t bosslevel = region_json["bosslevel"].asInt();
        if (bosslevel < 0) {
            UTIL_LOG(E, "invalid bosslevel");
            return false;
        }

        auto region = new region_t;
        region->proto = MAProto(BossLevel, bosslevel);
        if (NULL == region->proto) {
            safe_del(region);
            UTIL_LOG(E, "no proto bosslevel:%d", bosslevel);
            return false;
        }

        const Json::Value& pos_json = region_json["position"];
        region->pos.finish = pos_json["finish"].asInt64();
        if (!pos_json["occupant"].isNull()) {
            auto occupant = new player_t;
            if (!occupant->init_image(pos_json["occupant"])) {
                UTIL_LOG(E, "init occupant");
                return false;
            }

            region->pos.occupant = occupant;
            auto itr = player_occupied.find(occupant->id);
            if (itr == player_occupied.end())
                player_occupied[occupant->id] = region->proto->id;
            else {
                UTIL_LOG(E, "occupant:%d occupied multi times", occupant->id);
                return false;
            }
        }

        if (regions.get(bosslevel) != NULL) {
            UTIL_LOG(E, "region add multi times bosslevel:%d", bosslevel);
            return false;
        }

        regions.set(bosslevel, region);
    }
    return true;
}

void occupation_mgr::save(Json::Value &json) {
    json["objtype"] = "occupation";
    json["serverid"] = g_cfg->ServerId;

    Json::Value &json_arr = json["regions"];

    regions.for_each([&json_arr](int32_t bosslevelid, region_t* &region){
		if (region->pos.finish < 0)
			return;

		Json::Value region_json;
        region_json["bosslevel"] = region->proto->id;

		Json::Value &occupant_json = region_json["position"];
		occupant_json["finish"] = Json::Int64(region->pos.finish);
		if (NULL != region->pos.occupant)
			region->pos.occupant->save(occupant_json["occupant"]);

		json_arr.append(region_json);
    });
}

void occupation_mgr::update() {
	if (!g_console->_occupy_enabled) {
        //sess->send_notify("占领模块临时关闭");
        return;
	}

    list<int64_t> finished;
    regions.for_each([&finished](int32_t bosslevel, region_t* &region){
		auto occupant = region->pos.occupant;
		if (region->pos.finish < 0)
			return;

		if (region->pos.finish >= g_clock->tt_now)
			return;

		if (NULL == occupant || occupant->id < 0)
			return;

		finished.push_back(occupant->id);
    });

    for_each<int64_t>(finished, [this](int64_t &playerid){
        auto occupant = finish_occupy(playerid);
        safe_del(occupant);
    });

    static double save_timer = 30.0f;
    save_timer -= g_clock->delta;
    if (chged && save_timer <= 0.0f && g_dbf->ok()) {
        save_timer = 30.0f;
        Json::Value json;
        save(json);
        bool ret = g_dbf->set(
                fmt_cstr("_occuption:%d", g_cfg->ServerId),
                json,
                [](db_error_t err){
                    switch (err) {
                    case DBE_Success:
                        break;
                    default:
                        UTIL_LOG(E, "occupation_save error: %s", db_strerror(err));
                        break;
                    }
                });
        if (ret) {
            chged = false;
        }
    }
}

void occupation_mgr::destroy() {
    if (_inited && chged) {
    	if (!g_dbf->ok())
    		g_dbf->repair();
        Json::Value json;
        save(json);
        if (!g_dbf->set(
                fmt_cstr("_occuption:%d", g_cfg->ServerId),
                json,
                [](db_error_t err){
                    if (DBE_Success != err) {
                        UTIL_LOG(E, "occupation_save error: %s", db_strerror(err));
                        return;
                    }
                })) {
                    UTIL_LOG(E, "occupation save to db error");
                }
        g_dbf->wait();
    }

    regions.for_each([](int32_t bosslevelid, region_t* &region){
        auto occupant = region->pos.occupant;
        if (NULL != occupant) {
            safe_del(occupant);
        }
        safe_del(region);
    });
    regions.clear();
    player_occupied.clear();
}

void occupation_mgr::on_logon(player_t* player) {
    int32_t mapid = -1;

    auto itr = player_occupied.find(player->id);
    if (itr != player_occupied.end())
        mapid = itr->second;

    player->att(Pl4n_OccupiedMap, mapid);
}

player_t* occupation_mgr::finish_occupy(int64_t playerid, const char* hisname, int32_t* lastregion) {
    auto itr = player_occupied.find(playerid);
    if (player_occupied.end() == itr)
        return NULL;

    auto region = regions.get(itr->second);
    if (NULL == region)
        return NULL;

    position_t* pos = region->find_pos(playerid);
    if (pos == NULL)
        return NULL;

    if (lastregion)
    	*lastregion = itr->second;

    auto player = g_playermgr->get_player(playerid);

    player_t* occupant = pos->occupant;


    if (NULL != player)
        player->att(Pl4n_OccupiedMap, -1);

    player_occupied.erase(playerid);
    pos->occupant = NULL;

    // reward
    int32_t rewardid = region->proto->occupation_reward;
    double rewardpct = region->proto->occupation_mins;
    int allsecs = 60 * region->proto->occupation_mins;
    int lastsecs = g_clock->tt_now - (pos->finish - allsecs);
    pos->finish = -1;
    chged = true;

    g_mail->send_system(
            playerid,
            EMT_OccupationReward,
            hisname ? fmt_cstr("%d,%s", region->proto->id, hisname) : fmt_cstr("%d", region->proto->id),
            region->proto->occupation_reward,
            min<double>(1.0f, double(lastsecs)/allsecs));

    return occupant;
}

void occupation_mgr::send_occuptionstatus(client_session_t* sess, vector<int32_t> &levelids) {
    CREATE_CS_SMSG(SOccuptionStatus, occuptionstatus);

    for (auto levelid : levelids) {
        auto region = g_occupation->regions.get(levelid);

        if (NULL == region) {
    		auto ooccupant = occuptionstatus->add_occupant();
    		ooccupant->set_levelid(levelid);
    		ooccupant->set_finish(0);
        	continue;
        }

		if (region->pos.finish < 0) {
    		auto ooccupant = occuptionstatus->add_occupant();
    		ooccupant->set_levelid(levelid);
    		ooccupant->set_finish(0);
			continue;
		}

		auto occupant = region->pos.occupant;
		if (NULL == occupant) {
			auto ooccupant = occuptionstatus->add_occupant();
			ooccupant->set_levelid(levelid);
			ooccupant->set_finish(0);
			continue;
		}

		auto ooccupant = occuptionstatus->add_occupant();
		ooccupant->set_levelid(levelid);
		ooccupant->set_name(occupant->att(Plss_Name));
		ooccupant->set_finish(region->pos.finish);


		for (int j=0; j<PlayerCombatCSlotNum; ++j) {
			auto pet = occupant->pets.get(occupant->combat[j]);
			if (NULL == pet)
				continue;

			auto opet = ooccupant->add_pet();
			opet->set_pos(j);
			opet->set_level(pet->att(Pe4s_Level));
			opet->set_proto(pet->proto->id);
		}
    }

    sess->send_msg(___msg);
}
